/**
 * DispatcherServlet.java
 *
 * Copyright (C) 2009,  Tomislav Milkovic
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package ws4is.engine;


import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ws4is.engine.actions.AFilterAction;
import ws4is.engine.actions.AServletAction;
import ws4is.engine.enums.ActionScope;
import ws4is.engine.enums.HTTPRequestType;


/*
 * Generic servlet for dynamic request processing. Servlet parse url parameter
 * "action" which contains class name instance of ServletAction class.
 */
public abstract class ADispatcherServlet extends HttpServlet implements Servlet,IDispatcher {

	protected final Logger loger = LoggerFactory.getLogger(this.getClass());
	protected ConfigLoader engine;


	public void init(ServletConfig conf) throws ServletException {
		super.init(conf);
		engine = new ConfigLoader(conf.getServletContext(),this);	
	}

	
	@Override
	public void destroy() {
		engine.releaseResources();
		super.destroy();
	}

	
	public Map getSessionActions(HttpSession session){
		Map map = (Map) session.getAttribute(getClass().getCanonicalName());
		if(map==null){
			map = new HashMap();
			session.setAttribute(getClass().getCanonicalName(),map);
		}
		
		return map;
	}
	
	@Override
	public void callMethod(final String actionClass ,ConfigObject config, HTTPRequestType type,HttpServletRequest request, HttpServletResponse response,  final FilterChain chain)	throws Exception, IOException {

		Exception ex = null;
		try {
			if (actionClass == null) {
				actionNotFoundReponse(request,response);
				return;
			}		
			
			
			AServletAction actionObject = null;
			ActionScope as = config.getScope();
			switch(as) {
			   //global in context of this web application; stores to servlet application context
				case GLOBAL : {
					ServletContext ctx = request.getSession().getServletContext();
					Map<String,AServletAction>  globalServletActions = null;					
					globalServletActions = (Map<String, AServletAction>) ctx.getAttribute(getClass().getCanonicalName());
					if (globalServletActions==null){
						globalServletActions = new HashMap<String, AServletAction>();
						ctx.setAttribute(getClass().getCanonicalName(),globalServletActions);
					} else	actionObject = globalServletActions.get(actionClass);
					if (actionObject==null){
						Constructor<AServletAction> con = config.getActionConstructor();
						actionObject = con.newInstance();	
						globalServletActions.put(actionClass, actionObject );
					}				
					break;
				  }
				//on each request new action instantiation is performed
				case REQUEST :{
					Constructor<AServletAction> con = config.getActionConstructor();
					actionObject = con.newInstance();						
					break;
				  }
				//client session keeps action object; available in other actions of the same session
				case SESSION : {
					HttpSession ses = request.getSession();
					Map<String,AServletAction>  sessionServletActions = null;
					sessionServletActions = (Map<String, AServletAction>) ses.getAttribute(getClass().getCanonicalName());
					if (sessionServletActions==null){
						sessionServletActions = new HashMap<String, AServletAction>();
						ses.setAttribute(getClass().getCanonicalName(),sessionServletActions);
					} else	actionObject = sessionServletActions.get(actionClass);
					if (actionObject==null){
						Constructor<AServletAction> con = config.getActionConstructor();
						actionObject = con.newInstance();	
						sessionServletActions.put(actionClass, actionObject );
					}	
					break;
				  }
			}
			
	
			actionObject.execute(config, type, request, response, chain);
						
		} catch (Exception e) {
			ex = e;
			//TODO log error executing action
			//printMessage(response ,e.getMessage());
			throw e;
		} finally {
			postAction(type, actionClass, ex);
		}
		
	}


	protected void actionNotFoundReponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		engine.printMessage(response ,this.getClass().getSimpleName().concat(" has no regular parameter {action}!!!"));
	}

	
	protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try{
			engine.processAction(HTTPRequestType.GET, request, response);
		}catch(Exception e){
			engine.printMessage(response, e,null);
			loger.error("ERROR ON HTTP-GET",e);
		}
	}

	protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try{
			engine.processAction(HTTPRequestType.POST,request, response);
		}catch(Exception e){
			engine.printMessage(response, e,null);
			loger.error("ERROR ON HTTP-POST",e);
		}
	}

	protected final void doDelete(HttpServletRequest request,	HttpServletResponse response) throws ServletException, IOException {
		try{
			engine.processAction(HTTPRequestType.DELETE, request, response);
		}catch(Exception e){
			engine.printMessage(response, e,null);
			loger.error("ERROR ON HTTP-DELETE",e);
		}
	}

	protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try{
			engine.processAction(HTTPRequestType.PUT, request, response);
		}catch(Exception e){
			engine.printMessage(response, e,null);
			loger.error("ERROR ON HTTP-PUT",e);
		}
	}

	// protected void doTrace(HttpServletRequest request, HttpServletResponse
	// response)throws ServletException, IOException {
	// processAction(RequestType.TRACE,request, response);
	// }

	// protected void doOptions(HttpServletRequest request, HttpServletResponse
	// response) throws ServletException, IOException {
	// processAction(RequestType.OPTIONS,request, response);
	// }

	// protected void doHead(HttpServletRequest request, HttpServletResponse
	// response) throws ServletException, IOException {
	// processAction(RequestType.HEAD,request, response);
	// }

	// protected void service(HttpServletRequest request, HttpServletResponse
	// response) throws ServletException, IOException {
	// processAction(RequestType.SERVICE,request, response);
	// }

}
